#!/usr/bin/env python
################################################################################
#            __  __            _   ____        _ _ ____        _              #
#           |  \/  | ___  __ _| |_| __ )  __ _| | | __ )  ___ | |_           #
#           | |\/| |/ _ \/ _` | __|  _ \ / _` | | |  _ \ / _ \| __|         #
#           | |  | |  __/ (_| | |_| |_) | (_| | | | |_) | (_) | |_          #
#           |_|  |_|\___|\__,_|\__|____/ \__,_|_|_|____/ \___/ \__|          #
#                                                                             #
#                                     by                                       #
# _______ .______       _______  _______   ______   ______   _______   _______  #
#|   ____||   _  \     |   ____||   ____| /      | /  __  \ |       \ |   ____|  #
#|  |__   |  |_)  |    |  |__   |  |__   |  ,----'|  |  |  ||  .--.  ||  |__      #
#|   __|  |      /     |   __|  |   __|  |  |     |  |  |  ||  |  |  ||   __|      #
#|  |     |  |\  \----.|  |____ |  |____ |  `----.|  `--'  ||  '--'  ||  |____      #
#|__|     | _| `._____||_______||_______| \______| \______/ |_______/ |_______|    #
#                                                                                 #
#                           Identification Module                                #
#                             Modular FrameWork                                 #
#
# Coders
#
#	(Polarina)
#
# Debuggers
#
#	Justin Giorgi (rebel_kid)
#	Lachlan Gilmour (chilli0)
#	David Liedle (spiderbyte)
#	(Polarina)
#	(prologic)
#
################################################################################
#                                                                              #
#                               GPL Licensed                                   #
#                                                                              #
################################################################################

# Imports
import re
import traceback

match_expression = re.compile("^[ ]*((?P<sign>[-]?)(?P<bracket>[(])|(?P<number>[-]?[0-9]+\.[0-9]+|[-]?[0-9]+)|(?P<operator>[*/+-]))")
match_whitespace = re.compile("^[ ]*$")

class rational(object):
	def __init__(self, numerator, denominator = None):
		if denominator:
			self.numerator   = numerator
			self.denominator = denominator
		else:
			if "." in str(numerator):
				whole, fraction = numerator.split(".")
				
				self.numerator   = long(whole) * 10 ** len(fraction)
				self.denominator = 10 ** len(fraction)
				
				if self.numerator < 0:
					self.numerator -= long(fraction)
				else:
					self.numerator += long(fraction)
			else:
				self.numerator   = long(numerator)
				self.denominator = 1L
		
		self.simplify()
	
	def __str__(self):
		if self.numerator % self.denominator == 0:
			return str(self.numerator)
		
		if abs(self.numerator) > self.denominator:
			negative = ""
			
			if self.numerator < 0:
				negative = "-"
			
			return "%s%d %d/%d" % (
				negative,
				abs(self.numerator) / self.denominator,
				abs(self.numerator) % self.denominator,
				self.denominator
			)
		
		return "%d/%d" % (self.numerator, self.denominator)
	
	def __add__(self, other):
		return rational(
			self.numerator * other.denominator + self.denominator * other.numerator,
			self.denominator * other.denominator
		)
	
	def __sub__(self, other):
		return rational(
			self.numerator * other.denominator + self.denominator * -other.numerator,
			self.denominator * other.denominator
		)
	
	def __mul__(self, other):
		return rational(
			self.numerator * other.numerator,
			self.denominator * other.denominator
		)
	
	def __div__(self, other):
		if other.denominator == 0:
			raise ZeroDivisionError("cannot divide by zero")
		
		return rational(
			self.numerator * other.denominator,
			self.denominator * other.numerator
		)
	
	def simplify(self):
		a = self.numerator
		b = self.denominator
		
		while b != 0:
			a, b = b, a % b
		
		self.numerator   /= a
		self.denominator /= a

def parse_type(entry):
	if isinstance(entry, str):
		return "operator"
	elif isinstance(entry, rational):
		return "number"
	elif isinstance(entry, list):
		return "sub-expression"
	
	raise Exception("entry type unknown, %s" % type(entry))

def parse_bracket(expression, first):
	last  = first
	depth = 0
	
	for character in expression[first :]:
		if character == "(":
			depth += 1
		elif character == ")":
			depth -= 1
			
			if depth == 0:
				break
		
		last += 1
	
	return last

def parse(expression):
	result = []
	
	while expression.strip() != "":
		match = match_expression.match(expression)
		
		if not match:
			raise Exception("invalid expression")
		
		end = match.end() - 1
		
		if match.group("number"):
			if len(result) == 0 or parse_type(result[-1]) == "operator":
				result.append(rational(match.group("number")))
			elif len(result) > 0 and parse_type(result[-1]) == "number" and "-" in match.group("number"):
				result.append("-")
				result.append(rational(match.group("number")[1:]))
			else:
				raise Exception("unexpected number")
		elif match.group("operator"):
			if len(result) > 0 and (parse_type(result[-1]) == "number" or parse_type(result[-1]) == "sub-expression"):
				result.append(match.group("operator"))
			else:
				raise Exception("unexpected operator")
		elif match.group("bracket"):
			if len(result) > 0 and (parse_type(result[-1]) == "number" or parse_type(result[-1]) == "sub-expression"):
				result.append("*")
			
			end = parse_bracket(expression, match.end() - 1)
			sub = parse(expression[match.end() : end])
			
			if match.group("sign") == "-":
				result.append([0L, "-", sub])
			else:
				result.append(sub)
		
		expression = expression[end + 1 :]
	
	# We cannot end with an operator or an empty list.
	if len(result) == 0:
		raise Exception("invalid expression")
	elif parse_type(result[-1]) == "operator":
		raise Exception("unexpected operator")
	
	return result

def evaluate(expression):
	for i in expression:
		if isinstance(i, list):
			expression[expression.index(i)] = evaluate(i)
	
	while True:
		if "*" in expression and "/" in expression:
			pos = min(expression.index("*"), expression.index("/"))
		elif "*" in expression:
			pos = expression.index("*")
		elif "/" in expression:
			pos = expression.index("/")
		else:
			break
		
		if expression[pos] == "*":
			tmp = expression[pos - 1] * expression[pos + 1]
		else:
			tmp = expression[pos - 1] / expression[pos + 1]
		
		expression[pos - 1 : pos + 2] = [tmp]
	
	while True:
		if "+" in expression and "-" in expression:
			pos = min(expression.index("+"), expression.index("-"))
		elif "+" in expression:
			pos = expression.index("+")
		elif "-" in expression:
			pos = expression.index("-")
		else:
			break
		
		if expression[pos] == "+":
			tmp = expression[pos - 1] + expression[pos + 1]
		else:
			tmp = expression[pos - 1] - expression[pos + 1]
		
		expression[pos - 1 : pos + 2] = [tmp]
	
	return expression[0]

def calculate(globalvars, msgvars):

	# Starts calc script and returns answer	
	if features[2]:
		answer = evaluate(parse(' '.join(msgvars["command"][1:])))
		msgvars["server"].privmsg(msgvars["destination"], "%s: It's %s" % (msgvars["source"], str(answer)))
	else:
		server.privmsg(destination, "Sorry but that feature is disabled here.")
	
def autoload(globalvars):
	
	# Loads oncall functions
	oncall = {'calc':calculate, 'eval':calculate}
	for feature in oncall.items():
		if feature[0] not in globalvars["oncall"].keys():
			globalvars["oncall"][feature[0]] = feature[1]
	
def load(globalvars, msgvars):
	
	# Loads oncall functions
	oncall = {'calc':calculate, 'eval':calculate}
	for feature in oncall.items():
		if feature[0] not in globalvars["oncall"].keys():
			globalvars["oncall"][feature[0]] = feature[1]
		else:
			msgvars["server"].privmsg(msgvars["destination"], "There is already a function named %s." % (feature[0]))
	return True
			
def unload(globalvars, msgvars, reloadmodule = False):
	
	# Unloads oncall functions
	oncall = {'calc':calculate, 'eval':calculate}
	for feature in oncall.items():
		if feature[0] in globalvars["oncall"].keys():
			del globalvars["oncall"][feature[0]]
	return True
